package com.vhall.component.service.rbac.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.vhall.component.dao.rbac.*;
import com.vhall.component.entity.account.AdminsEntity;
import com.vhall.component.entity.account.MenuesEntity;
import com.vhall.component.entity.account.RoleEntity;
import com.vhall.component.entity.account.RoleMenuesEntity;
import com.vhall.component.entity.rbac.vo.*;
import com.vhall.component.framework.common.constant.CommonConstant;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.plugin.common.constant.RedisKey;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.plugin.common.support.Token;
import com.vhall.component.plugin.common.support.TokenThreadLocal;
import com.vhall.component.plugin.common.utils.Constants;
import com.vhall.component.service.rbac.RbacRoleActionService;
import com.vhall.component.service.rbac.RbacRoleMenuService;
import com.vhall.component.service.rbac.RbacRoleService;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.RandomStringGenerator;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static org.apache.commons.text.CharacterPredicates.DIGITS;
import static org.apache.commons.text.CharacterPredicates.LETTERS;

/**
 * @author dalong
 * @date 2021-05-11 16:31
 */
@Service
public class RbacRoleServiceImpl implements RbacRoleService {

    @Autowired
    private RbacRoleMapper rbacRoleMapper;

    @Autowired
    private RbacRoleActionService roleActionService;

    @Autowired
    private RbacRoleMenuService roleMenuService;

    @Autowired
    private RbacMenuMapper rbacMenuMapper;

    @Autowired
    private RbacRoleMenuesMapper rbacRoleMenuesMapper;

    @Autowired
    private RbacUserRoleMapper userRoleMapper;

    @Autowired
    private AccountAdminMapper accountAdminMapper;
    @Resource
    private RedisTemplate<String,Object> redistemplate;

    private RandomStringGenerator generator = new RandomStringGenerator.Builder()
            .withinRange('0', 'z')
            .filteredBy(LETTERS, DIGITS)
            .build();

    @Override
    public RoleAdminGetRspVO getRoleForAdmin(RoleAdminGetReqVO reqVO) {
        RoleAdminGetRspVO rspVO = new RoleAdminGetRspVO();
        Integer roleId = reqVO.getRoleId();
        LambdaQueryWrapper<RoleEntity> where = Wrappers.lambdaQuery();
        where.eq(RoleEntity::getRoleId, roleId)
                .isNull(RoleEntity::getDeletedAt)
                .last("limit 1");
        RoleEntity roleEntity = rbacRoleMapper.selectOne(where);
        if (roleEntity == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROLE);
        }
        // 权限处理
        List<Integer> actionIdList = roleActionService.getActionIdListByRoleId(roleId);
        List<ActionsRspVO> actionsList = roleActionService.getActionsListByActionIdList(actionIdList, roleId);
        // 菜单处理
        List<Integer> menuIdList = roleMenuService.getMenuIdListByRoleId(roleId);
        List<MenuesRspVO> menuesList = roleMenuService.getMenuesListByMenuIds(menuIdList, roleId);
        // 格式化数据
        BeanUtils.copyProperties(roleEntity, rspVO);
        String statusStr = getStatusStr(rspVO.getStatus());
        rspVO.setStatusStr(statusStr);
        rspVO.setActions(actionsList);
        rspVO.setMenues(menuesList);
        return rspVO;
    }

    @Override
    public RoleAdminListRspVO getRoleListForAdmin(RoleAdminListReqVO reqVO) {
        RoleAdminListRspVO rspVO = new RoleAdminListRspVO();
        LambdaQueryWrapper<RoleEntity> where = Wrappers.lambdaQuery();
        where.eq(reqVO.getStatus() != null, RoleEntity::getStatus, reqVO.getStatus())
                .isNull(RoleEntity::getDeletedAt)
                .ge(reqVO.getBeginTime() != null, RoleEntity::getCreatedAt, reqVO.getBeginTime());
        if (reqVO.getPage() == null) {
            reqVO.setPage(1);
        }
        if (reqVO.getEndTime() != null) {
            where.le(RoleEntity::getCreatedAt, getEndTime(reqVO.getEndTime()));
        }
        where.like(StringUtils.isNotBlank(reqVO.getKeyword()),RoleEntity::getName, reqVO.getKeyword());

        Integer total = rbacRoleMapper.selectCount(where);
        rspVO.setTotal(total);
        where.orderByDesc(RoleEntity::getRoleId);
        int currPage = reqVO.getPage() < 0 ? 1 : reqVO.getPage();
        rspVO.setCurrentPage(currPage);
        int pageSize = null == reqVO.getPageSize() ? 10 : reqVO.getPageSize();
        rspVO.setPerPage(pageSize);
        int offset = (currPage - 1) * pageSize;
        where.last("limit " + offset + "," + pageSize);
        List<RoleEntity> roleListByPage = rbacRoleMapper.selectList(where);

        List<RoleRspVO> list = new LinkedList<>();
        for (RoleEntity roleEntity : roleListByPage) {
            RoleRspVO roleRspVO = new RoleRspVO();
            BeanUtils.copyProperties(roleEntity, roleRspVO);
            roleRspVO.setStatusStr(getStatusStr(roleEntity.getStatus()));
            if (Objects.isNull(roleEntity.getType())) {
                roleRspVO.setTypeName("-");
            } else {
                if (roleEntity.getType() == 1) {
                    roleRspVO.setTypeName("平台级");
                } else {
                    roleRspVO.setTypeName("部门级");
                }
            }

            list.add(roleRspVO);
        }
        rspVO.setPath("/");
        Integer lastPage = total % pageSize == 0 ? total / pageSize : (total / pageSize) + 1;
        rspVO.setLastPage(lastPage);
        Integer firstItem = offset + 1;
        rspVO.setFrom(firstItem);
        Integer lastItem = firstItem + total - 1;
        rspVO.setTo(lastItem);
        rspVO.setFirstPageUrl(getUrl(1));
        rspVO.setLastPageUrl(getUrl(lastPage));
        if (lastPage > currPage) {
            rspVO.setLastPageUrl(getUrl(currPage + 1));
        }
        if (currPage > 1) {
            rspVO.setPrevPageUrl(getUrl(currPage - 1));
        }
        rspVO.setData(list);
        return rspVO;
    }

    @Override
    public RoleAdminAddRspVO addRoleForAdmin(RoleAdminAddReqVO reqVO) {
        Integer count = rbacRoleMapper.selectCount(Wrappers.<RoleEntity>lambdaQuery()
                .eq(RoleEntity::getName, reqVO.getName())
                .isNull(RoleEntity::getDeletedAt));
        if (count > 0) {
            throw new BusinessException(BizErrorCode.BIZ_ROLE_EXIST);
        }
        Token token = TokenThreadLocal.getToken();
        RoleEntity roleEntity = new RoleEntity();
        roleEntity.setName(reqVO.getName());
        roleEntity.setCode(generator.generate(6));
        roleEntity.setDesc(reqVO.getDesc());
        roleEntity.setLevel(reqVO.getLevel() == null ? 0 : reqVO.getLevel());
        roleEntity.setStatus(Constants.STATUS_ENABLED);
        roleEntity.setType(reqVO.getType());
        roleEntity.setAccountId(token.getAccountId());
        roleEntity.setAccountName(token.getNickname());
        LocalDateTime now = LocalDateTime.now();
        roleEntity.setCreatedAt(now);
        roleEntity.setUpdatedAt(now);
        int result = rbacRoleMapper.insert(roleEntity);
        if (result == 0) {
            throw new BusinessException(BizErrorCode.BIZ_ROLE_ADD_FAILED);
        }
        // 设置初始角色菜单权限
        Integer menuId = 1;

        MenuesEntity menuesEntity = rbacMenuMapper.selectById(menuId);
        if (menuesEntity != null) {
            RoleMenuesEntity roleMenuesEntity = new RoleMenuesEntity();
            roleMenuesEntity.setRoleId(roleEntity.getRoleId());
            roleMenuesEntity.setMenuId(menuId);
            roleMenuesEntity.setCreatedAt(now);
            roleMenuesEntity.setUpdatedAt(now);
            roleMenuService.insert(roleMenuesEntity);
        }
        // 设置初始角色操作权限
        roleActionService.initRoleActionList(roleEntity.getRoleId());
        // 格式化返回数据
        RoleAdminAddRspVO rspVo = new RoleAdminAddRspVO();
        BeanUtils.copyProperties(roleEntity, rspVo);
        switch (roleEntity.getStatus()) {
            case 0:
                rspVo.setStatusStr("关闭");
                break;
            case 1:
                rspVo.setStatusStr("开启");
                break;
            default:
                rspVo.setStatusStr(null);
        }
        return rspVo;
    }

    @Override
    public Integer editRoleForAdmin(RoleAdminEditReqVO reqVO) {
        List<RoleEntity> roles = rbacRoleMapper.selectList(Wrappers.<RoleEntity>lambdaQuery()
                .eq(RoleEntity::getName, reqVO.getName())
                .isNull(RoleEntity::getDeletedAt));
        Integer roleId = reqVO.getRoleId();
        if (CollectionUtils.isNotEmpty(roles) && roles.stream().anyMatch(x -> !roleId.equals(x.getRoleId()))) {
            throw new BusinessException(BizErrorCode.BIZ_ROLE_EXIST);
        }

        LambdaUpdateWrapper<RoleEntity> update = Wrappers.lambdaUpdate();
        update.set(StringUtils.isNotBlank(reqVO.getName()), RoleEntity::getName, reqVO.getName())
                .set(StringUtils.isNotBlank(reqVO.getDesc()), RoleEntity::getDesc, reqVO.getDesc())
                .set(RoleEntity::getUpdatedAt, LocalDateTime.now())
                .isNull(RoleEntity::getDeletedAt)
                .eq(roleId != null, RoleEntity::getRoleId, roleId);
        int update1 = rbacRoleMapper.update(null, update);
        if (update1>=1) {
            String key = RedisKey.ROLE_PERMISSION_MENUS+ roleId;
            redistemplate.delete(key);
        }
        return update1;
    }

    @Override
    public List<Integer> deleteRoleForAdmin(RoleAdminDeleteReqVO reqVO) {
        //分割roleIds
        List<Integer> roleIds = Splitter.on(",").splitToList(reqVO.getRoleIds()).parallelStream().map(s -> {
            if (StringUtils.isNotBlank(s)) {
                return Integer.parseInt(s.trim());
            }
            return null;
        }).distinct().collect(Collectors.toList());

        for (Integer integer : roleIds) {
            LambdaQueryWrapper<AdminsEntity> where = Wrappers.lambdaQuery();
            where.eq(AdminsEntity::getRoleId, integer)
                    .isNull(AdminsEntity::getDeletedAt)
                    .last("LIMIT 1");
            AdminsEntity adminsEntity = accountAdminMapper.selectOne(where);
            if (adminsEntity != null) {
                throw new BusinessException(BizErrorCode.BIZ_ROLE_ALREADY_BIND_NOT_DELETE);
            }
        }
        LambdaUpdateWrapper<RoleEntity> delete = Wrappers.lambdaUpdate();
        delete.in(RoleEntity::getRoleId, roleIds)
                .isNull(RoleEntity::getDeletedAt)
                .set(RoleEntity::getStatus, Constants.STATUS_DISABLED)
                .set(RoleEntity::getDeletedAt, LocalDateTime.now());
        rbacRoleMapper.update(null, delete);
        return roleIds;
    }

    @Override
    public RoleVO getInfo(Integer roleId) {
        LambdaQueryWrapper<RoleEntity> where = Wrappers.lambdaQuery();
        where.eq(RoleEntity::getRoleId, roleId)
                .isNull(RoleEntity::getDeletedAt)
                .last("LIMIT 1");
        RoleEntity roleEntity = rbacRoleMapper.selectOne(where);
        RoleVO roleVO = new RoleVO();
        BeanUtils.copyProperties(roleEntity, roleVO);
        return roleVO;
    }

    @Override
    public List<RoleVO> getRoleByApp(RoleGetListByAppReqVO reqVO) {
        LambdaQueryWrapper<RoleEntity> where = Wrappers.lambdaQuery();
        where.eq(RoleEntity::getAppId, reqVO.getAppId())
                .eq(RoleEntity::getStatus, Constants.STATUS_ENABLED)
                .isNull(RoleEntity::getDeletedAt);
        List<RoleVO> roleVoS = new ArrayList<>();
        List<RoleEntity> entities = rbacRoleMapper.selectList(where);
        for (RoleEntity roleEntity : entities) {
            RoleVO roleVO = new RoleVO();
            BeanUtils.copyProperties(roleEntity, roleVO);
            roleVoS.add(roleVO);
        }
        return roleVoS;
    }

    @Override
    public List<RoleVO> listAllRole() {
        LambdaQueryWrapper<RoleEntity> where = Wrappers.lambdaQuery();
        where.eq(RoleEntity::getStatus, Constants.STATUS_ENABLED)
                .isNull(RoleEntity::getDeletedAt);
        List<RoleVO> roleVoS = new ArrayList<>();
        List<RoleEntity> entities = rbacRoleMapper.selectList(where);
        for (RoleEntity roleEntity : entities) {
            RoleVO roleVO = new RoleVO();
            BeanUtils.copyProperties(roleEntity, roleVO);
            roleVoS.add(roleVO);
        }
        return roleVoS;
    }

    @Override
    public boolean update(RoleUpdateReqVO reqVO) {

        // 查询所有角色信息
        LambdaQueryWrapper<RoleEntity> where = Wrappers.lambdaQuery();
        where.eq(RoleEntity::getName, reqVO.getRoleName())
                .isNull(RoleEntity::getDeletedAt)
                .last("LIMIT 1");
        RoleEntity otherRoleEntity = rbacRoleMapper.selectOne(where);
        if (otherRoleEntity != null && !otherRoleEntity.getRoleId().equals(reqVO.getRoleId())) {
            throw new BusinessException(BizErrorCode.BIZ_ROLE_EXIST);
        }
        LambdaUpdateWrapper<RoleEntity> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(RoleEntity::getRoleId, reqVO.getRoleId())
                .eq(RoleEntity::getAppId, reqVO.getAppId())
                .isNull(RoleEntity::getDeletedAt)
                .set(RoleEntity::getName, reqVO.getRoleName());
        rbacRoleMapper.update(null, updateWrapper);
        return true;
    }

    @Override
    public RoleVO create(RoleCreateReqVO reqVO) {
        LambdaQueryWrapper<RoleEntity> where = Wrappers.lambdaQuery();
        where.eq(RoleEntity::getName, reqVO.getRoleName())
                .eq(RoleEntity::getAppId, reqVO.getAppId())
                .eq(RoleEntity::getStatus, Constants.STATUS_ENABLED)
                .isNull(RoleEntity::getDeletedAt)
                .last("LIMIT 1");
        RoleEntity roleEntity = rbacRoleMapper.selectOne(where);
        if (roleEntity != null) {
            throw new BusinessException(BizErrorCode.BIZ_ROLE_EXIST);
        } else {
            roleEntity = new RoleEntity();
            roleEntity.setAppId(reqVO.getAppId());
            roleEntity.setName(reqVO.getRoleName());

            roleEntity.setCode(generator.generate(6));
            roleEntity.setDesc("");
            roleEntity.setCreatedAt(LocalDateTime.now());
            roleEntity.setUpdatedAt(LocalDateTime.now());
            roleEntity.setStatus(Constants.STATUS_ENABLED);
            rbacRoleMapper.insert(roleEntity);
            roleEntity.setRoleId(roleEntity.getRoleId());
            roleEntity.setLevel(0);

            RoleVO roleVO = new RoleVO();
            BeanUtils.copyProperties(roleEntity, roleVO);
            return roleVO;
        }
    }

    @Override
    public boolean delete(RoleDeleteReqVO reqVO) {
        LambdaQueryWrapper<RoleEntity> where = Wrappers.lambdaQuery();
        where.eq(RoleEntity::getRoleId, reqVO.getRoleId())
                .isNull(RoleEntity::getDeletedAt)
                .eq(RoleEntity::getStatus, Constants.STATUS_ENABLED);
        RoleEntity entity = rbacRoleMapper.selectOne(where);
        if (entity != null) {
            LambdaUpdateWrapper<RoleEntity> updateWrapper = Wrappers.lambdaUpdate();
            updateWrapper.eq(RoleEntity::getRoleId, reqVO.getRoleId())
                    .eq(RoleEntity::getAppId, reqVO.getAppId())
                    .isNull(RoleEntity::getDeletedAt)
                    .set(RoleEntity::getStatus, Constants.STATUS_DISABLED)
                    .set(RoleEntity::getDeletedAt, LocalDateTime.now());
            rbacRoleMapper.update(null, updateWrapper);
        } else {
            throw new BusinessException(BizErrorCode.BIZ_ROLE_NOT_EXIST);
        }
        return true;
    }

    @Override
    public RoleVO getInfoById(Integer roleId) {
        LambdaQueryWrapper<RoleEntity> where = Wrappers.lambdaQuery();
        where.eq(RoleEntity::getRoleId, roleId)
                .isNull(RoleEntity::getDeletedAt)
                .last("LIMIT 1");
        RoleEntity roleEntity = rbacRoleMapper.selectOne(where);
        if (roleEntity == null) {
            return null;
        }
        RoleVO roleVO = new RoleVO();
        BeanUtils.copyProperties(roleEntity, roleVO);
        return roleVO;
    }

    @Override
    public Boolean editMenues(EditMenuesReqVO vo) {
        // role info
        RoleEntity role = rbacRoleMapper.selectById(vo.getRoleId());
        if (role == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROLE);
        }
        //save data
        ArrayList<Integer> ids = Lists.newArrayList();
        String[] menus = vo.getMenuIds().split(",");
        for (String menu : menus) {
            ids.add(Integer.valueOf(menu));
        }
        if (!ids.contains(1)) {
            ids.add(1);
        }
        Boolean res = refreshMenus(vo.getRoleId(), ids);
        if (Boolean.FALSE.equals(res)) {
            throw new BusinessException(BizErrorCode.BIZ_EDIT_FAILED);
        }else{
            redistemplate.delete(RedisKey.ROLE_PERMISSION_MENUS+vo.getRoleId());
        }
        return Boolean.TRUE;
    }

    /**
     * 构建status_str
     *
     * @param status status
     * @return status remark
     */
    private String getStatusStr(Integer status) {
        String statusStr;
        switch (status) {
            case 0:
                statusStr = "关闭";
                break;
            case 1:
                statusStr = "开启";
                break;
            default:
                statusStr = null;
        }
        return statusStr;
    }

    private String getUrl(Integer page) {
        if (page <= 0) {
            page = 1;
        }
        return "/?page=" + page;
    }

    /**
     * 构建 yyyy-MM-dd 23:59:59格式的LocalDateTime
     *
     * @param time
     * @return
     */
    private LocalDateTime getEndTime(LocalDateTime time) {
        String format = time.format(DateTimeFormatter.ofPattern(CommonConstant.DATETIME_FORMAT_ENDTIME));
        DateTimeFormatter df = DateTimeFormatter.ofPattern(CommonConstant.DATETIME_FORMAT);
        return LocalDateTime.parse(format, df);
    }

    public Boolean refreshMenus(Integer roleId, List<Integer> ms) {
        LambdaQueryWrapper<RoleMenuesEntity> qury = Wrappers.lambdaQuery();
        qury.eq(RoleMenuesEntity::getRoleId, roleId);
        rbacRoleMenuesMapper.delete(qury);
        for (Integer m : ms) {
            MenuesEntity one = rbacMenuMapper.selectById(m);
            if (one != null) {
                RoleMenuesEntity en = new RoleMenuesEntity();
                en.setRoleId(roleId);
                en.setMenuId(m);
                rbacRoleMenuesMapper.insert(en);
            }
        }
        return Boolean.TRUE;
    }
}
